%%%-------------------------------------------------------------------
%%% @author chenlong
%%% @copyright (C) 2019, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 21. 三月 2019 14:12
%%%-------------------------------------------------------------------
-module(timer_wheel).
-author("chenlong").

-include("common.hrl").

%% API
-export([init/0,work/1]).

-export([plan/2,cancel_plan/1]).

-define(timer_wheel_plan, timer_wheel_plan).


%%=============================EXPORTED FUNCTION====================================
%% 初始化时间轮
init() ->
	NowSec = util:now(),
	tick(NowSec).

%% 工作
work(LastTick) ->
	try
		NowSec = util:now(),
		if LastTick >= NowSec ->
			tick(NowSec);
			true ->
				work(LastTick + 1, NowSec)
		end
	catch
		Reason:Why:Stack ->
			?ERR("work Reason=~p,Why=~p,Stack=~p", [Reason, Why, Stack])
	end.

%% 定时器,
%% Fun(TarSec)
%% return: timerKey = {TarSec, Ref} | true
plan(TarSec, Fun) when is_function(Fun,0) orelse is_function(Fun, 1)->
	NowSec = util:now(),
	if TarSec =< NowSec ->
		work3(Fun, TarSec);
		true ->
			add_plan(TarSec, Fun)
	end.

%% 删除计划
cancel_plan({Sec,Ref}) ->
	Plans = get_plan(Sec),
	Plans2 = lists:keydelete(Ref, 1, Plans),
	set_plan(Sec, Plans2);
cancel_plan(_) -> ok.

%%=============================LOCAL FUNCTION====================================
%% 时间轮心跳
tick(Sec) ->
	erlang:send_after(1000, self(),{?timer_wheel_tick, Sec}).

%% 增加一个计划
%% return: timerKey = {TarSec, Ref}
add_plan(Sec, Fun) ->
	Ref = erlang:make_ref(),
	set_plan(Sec, [{Ref,Fun}|get_plan(Sec)]),
	{Sec,Ref}.

%% 清空某一秒安排的所有计划，并返回旧的计划
clear_plan(Sec) ->
	case erlang:erase({?timer_wheel_plan, Sec}) of
		A when is_list(A) ->
			A;
		_ ->
			[]
	end.

work(NowSec, NowSec) ->
	work2(NowSec),
	tick(NowSec);
work(LastTick, NowSec) ->
	work2(LastTick),
	work(LastTick+1, NowSec).

work2(Sec) ->
	Plans = clear_plan(Sec),
	lists:foreach(fun({_Ref, F}) ->
		work3(F,Sec)
	              end, Plans).

work3(F, Sec) when is_function(F, 1) ->
	?CATCH(F(Sec));
work3(F, _Sec) when is_function(F, 0) ->
	?CATCH(F()).

%%=============================INNER FUNCTION====================================
%% 设置一秒的安排的所有计划
set_plan(Sec, Plans) ->
	put({?timer_wheel_plan, Sec}, Plans).

%% 获取计划
get_plan(Sec) ->
	case erlang:get({?timer_wheel_plan, Sec}) of
		A when is_list(A) ->
			A;
		_ ->
			[]
	end.